'''
通过列表生成式，我们可以直接创建一个列表。但是，收内存限制，列表容量肯定是有限的。而且，创建一个包含100万个元素的列表
不仅占用恨得内存空间，如果我们 仅仅需要访问前面几个元素，那后面的绝大多数元素占用的空间都白白浪费了

所以，如果列表元素可以安装某种算法推算出来，那我们是否可以在循环的过程中不断推算出后续的元素呢？这样就不必创建完整的list
从而节省大量的空间，在Python中，这种一边循环一边计算的机制，称为生成器：generator
'''

# 要创建一个generator, 有很多种方法。第一种方法很简单，只要把一个列表生成式的[] 改成 ()，就创建了一个generator

g = (x * x for x in range(10))
print(g)  # 输出结果：<generator object <genexpr> at 0x000002E9E787C1E0>

# 我们可以使用next()函数，一个一个获取generator中的元素
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

print('-' * 100)
'''
0
1
4
9
16
25
36
'''

# 著名的：斐波拉契数列（Fibonacci）, 除第一个和第二个外，任意一个数都可以由前两个数相加得到：
'''
1,1,2,3,5,8,13,21,34,.....
斐波拉契数列用列表生成式写不出来，但是，用函数把它打印出来却很容易：

当输入1时，结果：
1
done

当输入4时，结果：
1
1
2
3
done
'''


# 打印斐波拉契数列的函数
def fib(max):
    # n, a, b = 0, 0, 1
    n = 0 # 定义一个计数器 n，表示已经打印了多少个数，初始为 0
    a = 0 # 定义变量 a，表示前一个数（初始为 0，因为第一个的前一个就可以赋值为0）
    b = 1 # 定义变量 b，表示当前要打印的数（初始为 1）
    while n < max:
        print(b)
        # 第一种写法，推荐此中写法
        a, b = b, a + b

        # 第二种写法，等价与上面的那种写法：a,b=b,a+b
        # tmp1 = b
        # tmp2 = a
        # a = tmp1
        # b = tmp2 + tmp1

        n = n + 1
    return 'done'
'''
代码解释：
注意，赋值语句：
a, b = b, a + b

相当于：
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
'''

print(fib(1))
print(fib(2))
print(fib(3))
print(fib(4))
print('-' * 100)
'''
仔细观察，可以看出，fib 函数实际上是定义了斐波拉契数列的推算规则，可以从第一个元素开始，推送出后续的任意元素。
这种逻辑其实非常类似generator。也就是说，上面的函数和generator仅一步之遥。要把fib函数变成generator函数，只需要把print(b) 改成 yield b 就可以了
'''

def fib2(max):
    n = 0 # 定义一个计数器 n，表示已经打印了多少个数，初始为 0
    a = 0 # 定义变量 a，表示前一个数（初始为 0，因为第一个的前一个就可以赋值为0）
    b = 1 # 定义变量 b，表示当前要打印的数（初始为 1）
    while n < max:
        # print(b)
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
print(fib2(1)) # 输出：<generator object fib2 at 0x0000026C9987E500>
g2 = fib2(1)
print(next(g2))
print('**********' * 10)
for i in fib2(5):
    print(i)

print('-' * 200)
'''
这里，最难理解的就是generator函数和普通函数的执行流程不一样。普通函数是顺序执行，遇到return语句或者最后一行函数语句就返回。
而变成generator的函数，在每次调用next()的时候执行，遇到 yield 语句返回，再次执行时，从上次返回的yield语句的地方继续执行
'''
# 举个简单饿例子，定义个generator函数，依次返回数字，1,3,5：
def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield 3
    print('step 3')
    yield 5
o = odd()
print(next(o))
print(next(o))
print(next(o))
